enum INPUT_SRC {
  I_STRING,
  I_READLINE,
  I_FILE
};

typedef struct {
  int type;
  // Next character to be read if > 0.
  int putback_ch;
  union {
    // I_STRING
    struct {
      // Pointer to beginning of string to read.
      char *str;
      // Pointer to current character to read in str.
      char *strp;
      // End of string used for "EOF" test.
      char *str_end;
    };
    // I_READLINE
    struct {
      // Prompt to display if more input required.
      char *prompt;
      // Pointer to beginning of current line begin read which is the
      // last line of input from user.
      char *rdln;
      // Pointer to current character to be read in rdln.
      char *rdlnp;
      // Did readline() return a NULL?  This exists because
      // rdln == NULL is one of the signals to get more input.
      int eof;
      // Flag to tell gr_get_char() to return a newline on
      // first encounter with end of string.
      int ret_nl;
    };
    // I_FILE
    struct {
      FILE *file;
    };
  };
} GEN_READ;

// Open a string for input. EOF == end of string.
int gr_open_str(GEN_READ *r, char *s);
// Open a file for input.
int gr_open_file(GEN_READ *r, char *pth);
// Open GNU readline for input. EOF == user presses ctrl-d
int gr_open_rdln(GEN_READ *r, char *prompt);
// Get character into *c.  Return nonzero if success.
int gr_get_char(GEN_READ *r, char *c);
// Free up resources held by r.
void gr_close(GEN_READ *r);
// End of input on r?
int gr_eof(GEN_READ *r);
// Put back a character onto the input.
// Note: any GEN_READ can have a char ungotten
//       even if not opened or in a valid state.
int gr_putback_char(GEN_READ *r, char c);
// Set prompt if interactive.
void gr_set_prompt(GEN_READ *r, char *prompt);

#define GR_NEXT_CHAR(r)\
    do {\
        char c;\
        gr_get_char(r, &c);\
        gr_putback_char(r, c);\
    } while (0)
